<template>
	<el-form
		class="bc-form"
		ref="formRef"
		:model="formModels"
		:inline="inline"
		:label-position="labelPosition"
		:label-width="labelWidth"
		:hide-required-asterisk="!isAsterisk"
		:size="size"
		:disabled="globalDisabled"
	>
		<slot
			v-for="item in formConfigList"
			:key="item.key"
			:name="item.key"
			:row="item"
		>
			<el-form-item
				:label="item.label"
				:prop="item.key"
				:rules="item.rules"
				v-if="!(item.visible === false)"
			>
				<template v-if="item.componentType == 'switch'">
					<el-switch v-model="formModels[item.key]" :disabled="item.disabled" />
				</template>
				<template v-else-if="item.componentType == 'date'">
					<el-date-picker
						class="flex"
						style="width: 100%"
						v-model="formModels[item.key]"
						:type="item.type"
						:placeholder="item.placeholder || '请选择'"
						:format="item.dateFormat || 'YYYY-MM-DD'"
						:value-format="item.dateFormat || 'YYYY-MM-DD'"
						:start-placeholder="item.startPlaceholder || '请选择开始日期'"
						:end-placeholder="item.endPlaceholder || '请选择结束日期'"
						:range-separator="item.separator || '-'"
						:disabled="item.disabled"
						:shortcuts="item.shortcuts || []"
					></el-date-picker>
				</template>
				<template v-else-if="item.componentType == 'radio'">
					<el-radio-group
						v-model="formModels[item.key]"
						:disabled="item.disabled"
					>
						<el-radio
							v-for="opt in item.options"
							:key="opt.value"
							:label="opt.value"
						>
							{{ opt.label }}
						</el-radio>
					</el-radio-group>
				</template>
				<template v-else-if="item.componentType == 'checkbox'">
					<el-checkbox-group
						v-model="formModels[item.key]"
						:disabled="item.disabled"
					>
						<el-checkbox
							v-for="opt in item.options"
							:key="opt.value"
							:label="opt.value"
						>
							{{ opt.label }}
						</el-checkbox>
					</el-checkbox-group>
				</template>
				<template v-else-if="item.componentType == 'stepper'">
					<el-input-number
						v-model="formModels[item.key]"
						:placeholder="item.placeholder || '请输入'"
						:disabled="item.disabled"
						:min="item.min"
						:max="item.max"
					/>
				</template>
				<template v-else-if="item.componentType == 'rate'">
					<el-rate v-model="formModels[item.key]" :disabled="item.disabled" />
				</template>
				<template v-else-if="item.componentType == 'slider'">
					<el-slider
						v-model="formModels[item.key]"
						:disabled="item.disabled"
						:min="item.min"
						:max="item.max"
					/>
				</template>
				<template v-else-if="item.componentType == 'uploader'">
					<el-upload
						drag
						action=""
						v-model:file-list="formModels[item.key]"
						:multiple="item.multiple"
						:accept="item.accept || 'image/*'"
						:http-request="afterRead(item)"
						:disabled="item.disabled"
						:limit="item.maxCount || 1"
						list-type="picture-card"
						:on-preview="handlePictureCardPreview"
					>
						<el-icon class="el-icon--upload"><upload-filled /></el-icon>
						<div class="el-upload__text">
							<em>点击拖拽上传</em>
						</div>
						<template #tip>
							<div class="el-upload__tip">
								{{ item.tip }}
							</div>
						</template>
					</el-upload>
				</template>
				<template v-else-if="item.componentType == 'select'">
					<el-select
						:class="{ 'bc-select': !item.remote }"
						@clear="
							item.confirmCallback &&
								item.confirmCallback(formModels[item.key], formModels, {})
						"
						style="width: 100%"
						v-model="formModels[item.key]"
						:placeholder="item.placeholder || '请选择'"
						:disabled="item.disabled"
						:multiple="item.multiple"
						:remote="item.remote"
						:loading="remoteMap[item.key] && remoteMap[item.key].remoteLoading"
						:remote-method="remoteMethod(item)"
						clearable
						filterable
					>
						<el-option
							@click="
								item.confirmCallback &&
									item.confirmCallback(formModels[item.key], formModels, opt)
							"
							v-for="opt in (remoteMap[item.key] && remoteMap[item.key].list) ||
							item.options"
							:key="opt.value"
							:label="opt.label"
							:value="opt.value"
						></el-option>
					</el-select>
				</template>
				<template v-else-if="item.componentType == 'sms'">
					<div class="flex" style="width: 100%">
						<el-input
							class="flex-1"
							v-model="formModels[item.key]"
							:type="item.type || 'text'"
							:maxlength="item.maxlength"
							:placeholder="item.placeholder || '请输入'"
							:disabled="item.disabled"
							:suffix-icon="item.suffixIcon"
							:prefix-icon="item.prefixIcon"
							:show-word-limit="item.showWordLimit"
							clearable
							@input="formatter(item.key, item.formatter)"
						/>
						<el-button
							class="ml-1"
							:disabled="
								countDownData.smSconfig.cumulative < countDownData.CUMULATIVE
							"
							type="primary"
							@click="handlerCountdown(item, countDownData.countdown)"
						>
							{{ countDownData.smSconfig.smsText }}
						</el-button>
					</div>
				</template>
				<el-input
					v-else
					v-model="formModels[item.key]"
					:type="item.type || 'text'"
					:maxlength="item.maxlength"
					:placeholder="item.placeholder || '请输入'"
					:disabled="item.disabled"
					:show-password="item.showPassword || false"
					:suffix-icon="item.suffixIcon"
					:prefix-icon="item.prefixIcon"
					:show-word-limit="item.showWordLimit"
					:rows="item.rows || 2"
					:autosize="item.autosize || { minRows: 2, maxRows: 4 }"
					clearable
					@input="formatter(item.key, item.formatter)"
				/>
			</el-form-item>
		</slot>
		<el-form-item>
			<el-button type="primary" :loading="loading" @click="submitForm()">
				{{ confirmButtonText }}
			</el-button>
			<el-button @click="resetForm()">{{ cancelButtonText }}</el-button>
		</el-form-item>
	</el-form>
	<el-dialog v-model="dialogVisible">
		<img :src="dialogImageUrl" style="width: 100%" />
	</el-dialog>
</template>
<script lang="ts" setup>
	import { defineProps, defineExpose, defineEmits, ref, reactive } from 'vue';
	import type { PropType } from 'vue';

	import { ElMessage } from 'element-plus';
	import { useCountDown } from '@/hooks/useCountDown';
	import { UploadFilled } from '@element-plus/icons-vue';
	const emits = defineEmits(['update:modelValue', 'apiSucceedCallback']);

	type OptionsType = {
		label: string;
		value: string | number;
	};

	type AutosizeType = {
		minRows: number;
		maxRows: number;
	};

	type FormConfigType = {
		componentType:
			| 'text'
			| 'sms'
			| 'switch'
			| 'checkbox'
			| 'radio'
			| 'stepper'
			| 'rate'
			| 'slider'
			| 'select'
			| 'date'
			| 'uploader';
		type?:
			| 'text'
			| 'textarea'
			| 'password'
			| 'normal'
			| 'today'
			| 'week'
			| 'next-month'
			| 'prev-month';
		key: string;
		defaultValue?: any;
		rules?: Array<object>;
		label?: string;
		visible?: boolean;
		maxlength?: number;
		placeholder?: string;
		disabled?: boolean;
		showPassword?: boolean;
		suffixIcon?: string;
		prefixIcon?: string;
		showWordLimit?: boolean;
		rows?: number;
		autosize?: AutosizeType;
		dateFormat?: 'YYYY-MM-DD' | 'YYYY-MM-DD HH:mm:ss';
		startPlaceholder?: string;
		endPlaceholder?: string;
		separator?: string;
		shortcuts?: Array<any>;
		options?: Array<OptionsType>;
		min?: number;
		max?: number;
		multiple?: number;
		accept?: number;
		maxCount?: number;
		tip?: string;
		remote?: boolean;
		confirmCallback(formModel: any, formModels: any, opt: any): void;
		formatter?: (str: string) => string;
	};

	const props = defineProps({
		modelValue: {
			type: Object,
		},
		// 行内表单模式
		inline: {
			type: Boolean,
			default: false,
		},
		// 表单域标签的位置 'left' | 'right' | 'top'
		labelPosition: {
			type: String,
			default: 'right',
		},
		// label=宽度
		labelWidth: {
			type: [Number, String],
			default: 'auto',
		},
		// 是否必须星号
		isAsterisk: {
			type: Boolean,
			default: true,
		},
		// 用于控制该表单内组件的尺寸	'large' | 'default' | 'small'
		size: {
			type: String,
			default: 'default',
		},
		// 禁用表单
		globalDisabled: {
			type: Boolean,
			default: false,
		},
		// 确认按钮文字
		confirmButtonText: {
			type: String,
			default: '提交',
		},
		// 取消按钮文字
		cancelButtonText: {
			type: String,
			default: '重置',
		},
		// 回显的数据，会覆盖defaultValue
		formModelValues: {
			type: Object as any,
		},
		// 表单模型
		formConfigList: {
			type: Array as PropType<Array<FormConfigType>>,
			required: true,
		},
		// 文件上传
		handlerUpload: {
			type: Function as PropType<
				(file: File, formConfig: FormConfigType) => Promise<any>
			>,
		},
		// 表单提交
		handlerSubmit: {
			type: Function as PropType<(formModels: object) => Promise<any>>,
			required: true,
		},
		// 重置回调
		resetForm: {
			type: Function,
		},
	});
	// 表单引用
	let formRef = ref();
	// 表单模型
	let formModels = reactive({});

	let loading = ref(false);

	// 存储远程搜索信息
	let remoteMap = ref({});
	// 图片放大
	const dialogVisible = ref(false);
	const dialogImageUrl = ref('');

	let init = () => {
		props.formConfigList.forEach(item => {
			// 默认值
			if (item.componentType === 'switch') {
				formModels[item.key] =
					typeof item.defaultValue === 'undefined' ? false : item.defaultValue;
			} else if (item.componentType === 'checkbox') {
				formModels[item.key] =
					typeof item.defaultValue === 'undefined' ? [] : item.defaultValue;
			} else if (item.componentType === 'radio') {
				formModels[item.key] =
					typeof item.defaultValue === 'undefined' ? '' : item.defaultValue;
			} else if (item.componentType === 'rate') {
				formModels[item.key] =
					typeof item.defaultValue === 'undefined' ? 0 : item.defaultValue;
			} else if (item.componentType === 'stepper') {
				formModels[item.key] =
					typeof item.defaultValue === 'undefined' ? 0 : item.defaultValue;
			} else if (item.componentType === 'slider') {
				formModels[item.key] =
					typeof item.defaultValue === 'undefined' ? 0 : item.defaultValue;
			} else if (item.componentType === 'uploader') {
				formModels[item.key] =
					typeof item.defaultValue === 'undefined' ? [] : item.defaultValue;
			} else if (item.componentType === 'select') {
				let checkValue = typeof item.defaultValue === 'undefined';
				// 远端搜索
				if (item.remote) {
					remoteMap.value[item.key] = {
						remoteLoading: false,
						list: [],
					};
				}
				if (item.multiple) {
					formModels[item.key] = checkValue ? [] : item.defaultValue;
				} else {
					formModels[item.key] = checkValue ? '' : item.defaultValue;
				}
			} else {
				formModels[item.key] = item.defaultValue || '';
			}
			// 回显数据
			if (
				props.formModelValues &&
				props.formModelValues[item.key] !== undefined
			) {
				let formModelValue = props.formModelValues[item.key];
				if (formModelValue && item.formatter) {
					// 数据格式化
					formModelValue = formModels[item.key] =
						item.formatter(formModelValue);
				}
			}
			emits('update:modelValue', formModels);
		});
	};
	init();
	// 输入限制
	let formatter = (key, callback) => {
		if (callback) {
			formModels[key] = callback(formModels[key]);
		}
	};

	const handlePictureCardPreview = (uploadFile: any) => {
		dialogImageUrl.value = uploadFile.url;
		dialogVisible.value = true;
	};

	// 远程搜索
	let remoteMethod = formConfig => {
		return async query => {
			if (query !== '') {
				try {
					remoteMap.value[formConfig.key].remoteLoading = true;
					remoteMap.value[formConfig.key].list = await formConfig.getOptions();
					remoteMap.value[formConfig.key].remoteLoading = false;
				} catch (error) {
					remoteMap.value[formConfig.key].remoteLoading = false;
				}
			} else {
				remoteMap.value[formConfig.key].list = [];
				remoteMap.value[formConfig.key].remoteLoading = false;
			}
		};
	};

	// 倒计时处理
	let countDownData = useCountDown();
	let handlerCountdown = async (formConfig, countdown) => {
		if (countDownData.smSconfig.cumulative !== countDownData.CUMULATIVE) {
			return ElMessage.warning('请倒计时结束后在点击');
		}
		await formRef.value.validateField(formConfig.beforeValidateKey);
		formConfig.handlerCountdown(countdown);
	};
	// 文件上传
	const afterRead = formConfig => {
		return async file => {
			if (formConfig.beforeRead) {
				let flags = formConfig.beforeRead(file);
				if (!flags) {
					return Promise.reject();
				}
			}
			if (props.handlerUpload) {
				return await props.handlerUpload(file, formConfig);
			}
		};
	};

	// 提交
	const submitForm = () => {
		formRef.value.validate(async valid => {
			if (valid) {
				loading.value = true;
				let params = {};
				Object.keys(formModels).forEach(key => {
					if (
						formModels[key] !== '' &&
						formModels[key] !== 'null' &&
						formModels[key] !== 'undefined' &&
						formModels[key] !== null &&
						formModels[key] !== undefined
					) {
						params[key] = formModels[key];
					}
				});
				try {
					await props.handlerSubmit(params);
					emits('apiSucceedCallback');
				} catch (error) {
					console.log(error);
				} finally {
					loading.value = false;
				}
			}
		});
	};
	// 重置
	const resetForm = () => {
		formRef.value.resetFields();
		props.resetForm && props.resetForm();
	};
	defineExpose({
		loading,
	});
</script>
<style lang="scss">
	.bc-form {
		.is-drag {
			width: 200px;
			height: 200px;
		}
		.el-upload-dragger {
			border: 0px !important;
		}
		.el-upload-list--picture-card {
			--el-upload-list-picture-card-size: 200px;
			--el-upload-list-picture-card-size: 200px;
		}
		.el-form-item__label {
			font-weight: bold;
		}
	}
	.bc-form.el-form--inline {
		.el-input__suffix {
			position: absolute;
			right: 11px;
		}
	}
</style>
